<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/11/16
 * Time: 14:44
 */
namespace fastpay;
date_default_timezone_set("Asia/Shanghai");
use think\Exception;

class Communal
{
    protected $config;
    public function __construct()
    {
        $this->config=Config::config();
    }
    /**
     * 获取公钥
     * @param $path
     * @return mixed
     * @throws Exception
     */
    public function loadX509Cert($path)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new Exception('loadx509Cert::file_get_contents ERROR');
            }

            $cert = chunk_split(base64_encode($file), 64, "\n");
            $cert = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";

            $res = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);

            if (!$detail) {
                throw new Exception('loadX509Cert::openssl_pkey_get_details ERROR');
            }

            return $detail['key'];
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * 获取客户端ip
     * @return string
     */
    public function get_client_ip() {
        if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
            $ip = getenv('HTTP_CLIENT_IP');
        } elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        } elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
            $ip = getenv('REMOTE_ADDR');
        } elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : '';
    }
    /**
     * 获取私钥
     * @param $path
     * @param $pwd
     * @return mixed
     * @throws Exception
     */
    public function loadPk12Cert($path, $pwd)
    {
        try {
            $file = file_get_contents($path);
            if (!$file) {
                throw new \Exception('loadPk12Cert::file_get_contents');
            }

            if (!openssl_pkcs12_read($file, $cert, $pwd)) {
                throw new \Exception('loadPk12Cert::openssl_pkcs12_read ERROR');
            }
            return $cert['pkey'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 私钥签名
     * @param $plainText
     * @param $path
     * @return string
     * @throws Exception
     */
    public function sign($plainText, $path)
    {
        $plainText = json_encode($plainText);
        try {
            $resource = openssl_pkey_get_private($path);
            $result = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);

            if (!$result) {
                throw new \Exception('签名出错' . $plainText);
            }

            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 公钥验签
     * @param $plainText
     * @param $sign
     * @param $path
     * @return int
     * @throws Exception
     */
    public function verify($plainText, $sign, $path)
    {
        $resource = openssl_pkey_get_public($path);
        $result = openssl_verify($plainText, base64_decode($sign), $resource);
        openssl_free_key($resource);

        if (!$result) {
            throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign, '02002');
        }

        return $result;
    }

    /**
     * 发送请求
     * @param $url
     * @param $param
     * @return bool|mixed
     * @throws Exception
     */
    public function http_post_json($url, $param)
    {
        if (empty($url) || empty($param)) {
            return false;
        }
        $param = http_build_query($param);
        try {

            $ch = curl_init();//初始化curl
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            //正式环境时解开注释
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data = curl_exec($ch);//运行curl
            curl_close($ch);

            if (!$data) {
                throw new \Exception('请求出错');
            }

            return $data;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    public function parse_result($result)
    {
        $arr = array();
        $response = urldecode($result);
        $arrStr = explode('&', $response);
        foreach ($arrStr as $str) {
            $p = strpos($str, "=");
            $key = substr($str, 0, $p);
            $value = substr($str, $p + 1);
            $arr[$key] = $value;
        }

        return $arr;
    }

    public function post_data($data,$sign){
       return $post = array(
            'charset' => 'utf-8',
            'signType' => '01',
            'data' => json_encode($data),
            'sign' => $sign
        );
    }

    public function check_key($data,$type){
        switch ($type){
            case 1:#支付
                $type='/order/pay';
                break;
            case 2:#订单查询
                $type='/order/pay';
                break;
        }
        // step2: 私钥签名
        $prikey = $this->loadPk12Cert($this->config['pri_key_path'], $this->config['cert_pwd']);
        $sign = $this->sign($data, $prikey);
        // step3: 拼接post数据
        $post =$this->post_data($data,$sign);
        // step4: post请求
        $result = $this->http_post_json($this->config['api_host'] . $type, $post);
        $arr = $this->parse_result($result);
        //step5: 公钥验签
        $pubkey = $this->loadX509Cert($this->config['pub_key_path']);
        try {
            $this->verify($arr['data'], $arr['sign'], $pubkey);
            return $arr;
        } catch (Exception $e) {
            return $e->getMessage();
        }
    }

    public function createAutoFormHtml($params, $reqUrl) {
        $encodeType = isset ( $params ['encoding'] ) ? $params ['encoding'] : 'UTF-8';
        $html = <<<eot
        <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset={$encodeType}" />
        </head>
        <body onload="javascript:document.sandpay.submit();">
         <form id="sandpay" name="sandpay" action="{$reqUrl}" method="post">
eot;
        foreach ( $params as $key => $value ) {
            $html .= " <textarea name=\"{$key}\" hidden='hidden'  id=\"{$key}\">{$value}</textarea>\n";
        }
        $html .= <<<eot
            </form>
        </body>
        </html>
eot;
        return $html;
    }

    /**
     * 订单查询
     */
    public function query($order_id){
        $data = array(
            'head' => array(
                'version' => '1.0',
                'method' => 'sandpay.trade.query',
                'productId' => '00000016',
                'accessType' => '1',
                'mid' => $this->config['mid'],
                'channelType' => '07',
                'reqTime' => date('YmdHis', time())
            ),
            'body' => array(
                'orderCode' => $order_id,//商户订单号
                'extend' => ''
            )
        );
        // step2: 私钥签名
        $prikey = $this->loadPk12Cert($this->config['pri_key_path'], $this->config['cert_pwd']);
        $sign = $this->sign($data, $prikey);
        // step3: 拼接post数据
        $post = array(
            'charset' => 'utf-8',
            'signType' => '01',
            'data' => json_encode($data),
            'sign' => urlencode($sign)
        );
        // step4: post请求
        $result = $this->http_post_json('https://cashier.sandpay.com.cn/gateway/api/order/query', $post);
        $arr = $this->parse_result($result);
        $data = json_decode($arr['data'], true);
        return $data;
    }
}